#include "u_service.h"

#include <istream>
#include <cassert>
using namespace std;

#include <boost/bind.hpp>
#include "serial_surpport.h"
#include "i_package.h"

UdpService::UdpService(unsigned short port, boost::asio::io_service& ioService) : \
	socket_(ioService, udp::endpoint(udp::v4(), port))
{
}

void UdpService::start()
{
	scheduleReceive();
}

void UdpService::scheduleReceive()
{
	socket_.async_receive_from(recvBuf_.prepare(MaxPackageSize), currentRemoteAddr_,
		boost::bind(&UdpService::handleReceive, shared_from_this(),
		boost::asio::placeholders::error,
		boost::asio::placeholders::bytes_transferred));
}

void UdpService::handleReceive(const boost::system::error_code& error, std::size_t byteTransferred)
{
	 if (!error || error == boost::asio::error::message_size)
	 {
		if(!packageHandler_)
		{
			return;
		}
		
		recvBuf_.commit(byteTransferred);
		istream is(&recvBuf_);

		Header curHeader;

		IPackage::Ptr package;
		if(recvBuf_.size() >= sizeof(Header))
		{
			serial::read(is, curHeader);
			if(recvBuf_.size() >= static_cast<size_t>(curHeader.size_))
			{
				package = IPackage::read(is, curHeader);
			}
		}
		packageHandler_->handle(currentRemoteAddr_, package, shared_from_this(), curHeader.seq_);
	 }
	 scheduleReceive();
}

void UdpService::setPackageHandler(boost::shared_ptr<IPackageReceiver> packageHandler)
{
	packageHandler_ = packageHandler;
}

UdpService::Ptr UdpService::newInstance(unsigned short port, boost::asio::io_service& ioService)
{ 
	return UdpService::Ptr(new UdpService(port, ioService));
}

void UdpService::send(const udp::endpoint& addr, boost::asio::const_buffers_1& package)
{
	socket_.send_to(package, addr);
}

void UdpService::send(const udp::endpoint& addr, IPackage::Ptr package, unsigned int seq)
{
	boost::asio::streambuf buf;
	ostream os(&buf);
	IPackage::write(os, *package, seq);
	socket_.send_to(buf.data(), addr);
}

boost::asio::io_service& UdpService::ioService()
{
	return socket_.get_io_service();
}

const int UdpService::MaxPackageSize = 65536;